import json
import time
from django.http import JsonResponse, HttpResponse
from django.shortcuts import render
from kubeclient import views as kube
import requests
import subprocess
import os
from . import models
from pathlib import Path
from ruamel import yaml
import operator

# Function's name will be append this list, when function be deleted.
except_list = []

# manager code_editor
""" construct
- user1
  - code_editor1 : remained_time
  - code_editor2 : remained_time
  ...
- user2
 ...
"""
code_editors_lifetime = {}
# ten minutes
lifetime = 3


# return render function page
def render_page(request):
    username = request.GET['username']
    return render(request, 'function-list.html',
                  {'username': username, 'function_infos': get_openfaas_functions_info(username)})


def get_dashboard_for_special_function_page(request):
    username = request.GET['username']
    selected_function_name = request.GET['selected_function_name']
    functions_info = get_openfaas_functions_info(username)
    functions_name = [function_info['name'] for function_info in functions_info]
    instance_for_special_function = {}
    for function_info in functions_info:
        instance_for_special_function[function_info['name']] = function_info['availableReplicas']
    selected_function_instances = 0
    if not selected_function_name:
        if len(functions_info):
            selected_function_name = functions_info[0]['name']
            selected_function_instances = functions_info[0]['availableReplicas']
        else:
            selected_function_name = ''
            selected_function_instances = 0
    else:
        selected_function_instances = instance_for_special_function[selected_function_name]
    return render(request, 'dashboard-for-special-function.html', {
        'username': username,
        'selected_function_name': selected_function_name,
        'selected_function_instances': selected_function_instances,
        'functions_name': functions_name
    })


def get_deployed_function_info(request):
    username = request.GET['username']
    functions_info = get_openfaas_functions_info(username)
    functions_name = []
    functions_invocation = []
    functions_availableReplicas = []
    instance_for_special_function = {}
    for function_info in functions_info:
        functions_name.append(function_info['name'])
        functions_invocation.append(function_info['invocationCount'])
        functions_availableReplicas.append(function_info['availableReplicas'])
        instance_for_special_function[function_info['name']] = function_info['availableReplicas']
    if len(functions_invocation):
        max_function_invocation = max(functions_invocation)
    else:
        max_function_invocation = 10
    return JsonResponse(
        {
            'deployed_function_count': len(functions_info),
            'functions_info': functions_info,
            'functions_name': functions_name,
            'functions_invocation': functions_invocation,
            'max_function_invocation': max_function_invocation,
            'functions_availableReplicas': functions_availableReplicas,
            'instance_for_special_function': instance_for_special_function,
        }
    )


def get_specify_function_info(request):
    function_name = request.GET['function_name']
    function_info_url = 'http://192.168.205.128:31112/system/function/' + function_name + '?namespace=openfaas-fn'
    try:
        response = requests.get(function_info_url, headers=headers, cookies=openfaas_login())
        response.raise_for_status()
        bytes = response.content
        function_info_str = bytes.decode('UTF-8')
        function_info_dir = json.loads(function_info_str)
        return JsonResponse(function_info_dir)
    except:
        return None


def function_detail_page(request):
    function_name = request.GET['function_name']
    return render(request, 'function-detail.html', {'function_name': function_name})


# return function workspace page
def function_workspace_page(request):
    username = request.GET['username']
    function_name = request.GET['function_name']
    return render(request, 'function-workspace.html', {'username': username, 'function_name': function_name})


# return function workspace main page
def function_workspace_main_page(request):
    username = request.GET['username']
    function_name = request.GET['function_name']
    return render(request, 'function-workspace-main.html', {'username': username, 'function_name': function_name})


def function_configure_page(request):
    username = request.GET['username']
    function_name = request.GET['function_name']
    return render(request, 'function-configure.html', {'username': username, 'function_name': function_name})


# deploy new function
def deploy_new_function(request):
    return render(request, 'deploy-new-function.html')


def forms_wizard_page(request):
    return render(request, 'forms-wizard.html')


def function_deploy_step_one(request):
    username = request.GET['username']
    return render(request, 'function-deploy-step-one.html', {'username': username})


def function_deploy_step_three(request):
    username = request.GET['username']
    function_url = request.GET['function_url']
    function_name = request.GET['function_name']
    function_lang = request.GET['function_lang']
    return render(request, 'function-deploy-step-three.html',
                  {
                      'function_url': function_url,
                      'username': username,
                      'function_name': function_name,
                      'function_lang': function_lang
                  })


def wait_editor(request):
    return render(request, 'wait-editor.html')


def code_editor_ready(request):
    url = 'http://192.168.205.128:30443'
    status = False
    try:
        response = requests.get(url)
        if response.status_code == 200:
            status = True
    except:
        status = False
    return JsonResponse({'status': status})


headers = {
    'Authorization': 'Basic YWRtaW46YWRtaW4='
}


def openfaas_login():
    login_url = "http://192.168.205.128:31112/ui/"
    try:
        response = requests.get(login_url, headers=headers)
        response.raise_for_status()
        return response.cookies
    except:
        return None


def get_openfaas_functions_info(username):
    function_info_url = 'http://192.168.205.128:31112/system/functions'
    try:
        response = requests.get(function_info_url, headers=headers, cookies=openfaas_login())
        response.raise_for_status()
        bytes = response.content
        function_info_str = bytes.decode('UTF-8')
        function_info_dir = json.loads(function_info_str)
        # print(function_info_dir[0]['name'])
        function_info_except_dir = [function_info for function_info in function_info_dir if
                                    function_info['name'] not in except_list and function_info['name'].split('-')[
                                        0] == username]
        info_list = sorted(function_info_except_dir, key=operator.itemgetter('name'))
        return info_list
    except:
        return None


def get_openfaas_functions_info2(request):
    function_info_url = 'http://192.168.205.128:31112/system/functions'
    function_info_dir = ''
    try:
        response = requests.get(function_info_url, headers=headers, cookies=openfaas_login())
        response.raise_for_status()
        function_info_str = response.content.decode('UTF-8')
        function_info_dir = json.loads(function_info_str)
    except:
        pass
    finally:
        return HttpResponse(json.dumps(function_info_dir))


def check_function_name_is_exits(request):
    function_infos = get_openfaas_functions_info()
    function_name_list = []
    for function_info in function_infos:
        function_name_list.append(function_info['name'])
    function_name = request.GET['fname']
    if function_name in function_name_list:
        return JsonResponse({"result": True})
    else:
        return JsonResponse({"result": False})


def create_function_by_template(request):
    username = request.POST['username']
    function_name = username + '-' + request.POST['fname']
    function_language = request.POST['flanguage']
    base_dir = '/usr/serverlessPlatform'
    function_url = 'http://' + str(request.environ['HTTP_HOST']).split(':')[0] + ":" + "31112/function/" + function_name
    try:
        # 1. clear old dir and create new dir
        function_dir = base_dir + '/' + 'workspace' + '/' + username + '/' + function_name
        if os.path.exists(function_dir):
            rm_cmd = 'rm -r %s' % function_dir
            subprocess.check_call(rm_cmd, shell=True, cwd=function_dir)
        os.makedirs(function_dir)

        # 2. copy template to dir
        template_dir = base_dir + '/' + 'function' + '/' + 'template'
        mov_dir_cmd = 'cp -r %s %s' % (template_dir, function_dir)
        subprocess.check_call(mov_dir_cmd, shell=True)

        # 3. generate function by template
        faas_new_cmd = 'faas new %s --lang %s -p 192.168.205.128:30002/openfaas-fn -g http://192.168.205.128:31112' % (
            function_name, function_language)
        subprocess.check_call(faas_new_cmd, shell=True, cwd=function_dir)

        # 4. create code editor
        if_exist_rm = True
        kube.code_editor_v2(username, function_name, function_language, if_exist_rm)

        # 5. update databases
        function = models.FunctionInfo.objects.filter(username=username, function_name=function_name)
        if function.exists():
            function.update(function_language=function_language)
        else:
            models.FunctionInfo.objects.create(username=username, function_name=function_name,
                                               function_language=function_language)
        # 6. jump code editor page
        return render(request, 'function-deploy-step-two.html',
                      {
                          'function_url': function_url,
                          'username': username,
                          'function_name': function_name,
                          'function_lang': function_language
                      })
    except:
        return JsonResponse({'status': False})


def modify_function(request):
    username = request.GET['username']
    function_name = request.GET['function_name']
    function = models.FunctionInfo.objects.filter(username=username, function_name=function_name)
    if function.exists():
        function_language = function[0].function_language
    else:
        function_language = 'python'

    function_url = 'http://' + str(request.environ['HTTP_HOST']).split(':')[0] + ":" + "31112/function/" + function_name
    # 1. create code editor
    if_exist_rm = False
    kube.code_editor_v2(username, function_name, function_language, if_exist_rm)
    # 5. jump code editor page
    return render(request, 'function-modify.html',
                  {
                      'function_url': function_url,
                      'username': username,
                      'function_name': function_name,
                      'function_lang': function_language
                  })


def build_function(request):
    base_dir = '/usr/serverlessPlatform'
    # parse parameters
    username = request.POST['username']
    function_name = request.POST['function_name']
    function_dir = base_dir + '/' + 'workspace' + '/' + username + '/' + function_name

    succeed = ''
    info = ''
    try:
        # build images
        faas_build_cmd = 'faas build -f %s' % (function_name + '.yml')
        info = subprocess.run(faas_build_cmd, shell=True, cwd=function_dir, stdout=subprocess.PIPE,
                              encoding="utf-8").stdout
        # clear images
        find_tag_equal_node_image_cmd = "docker images | grep none | awk '{print $3}'"
        tag_equal_node_images = subprocess.run(find_tag_equal_node_image_cmd, shell=True, stdout=subprocess.PIPE).stdout
        tag_equal_node_images_list = bytes.decode(tag_equal_node_images).replace('\n', ' ')
        if tag_equal_node_images_list:
            rm_tag_equal_node_image_cmd = 'docker rmi %s' % tag_equal_node_images_list
            subprocess.run(rm_tag_equal_node_image_cmd, shell=True, stdout=subprocess.PIPE, encoding="utf-8")
        succeed = True
    except Exception as e:
        info = str(e)
        succeed = False
    finally:
        return JsonResponse({'succeed': succeed, 'info': info})


def deploy_function(request):
    info = ''
    succeed = ''
    base_dir = '/usr/serverlessPlatform'
    # parse parameters
    username = request.POST['username']
    function_name = request.POST['function_name']
    function_dir = base_dir + '/' + 'workspace' + '/' + username + '/' + function_name
    function_image_name = '192.168.205.128:30002/openfaas-fn' + '/' + function_name
    try:
        # 1. login harbor
        login_harbor_cmd = 'docker login 192.168.205.128:30002 --username lgw --password Liuguowei89'
        # 2. push image
        push_image_cmd = 'docker push %s' % function_image_name
        # 3. login openfaas
        login_faas_cmd = 'faas-cli login --username admin --password admin -g 192.168.205.128:31112'
        # 4. deploy to openfaas
        deploy_openfaas_cmd = 'faas-cli deploy -f %s' % (function_name + '.yml')

        # 5. execute cmd
        info = subprocess.run(login_harbor_cmd +
                              ' && ' + push_image_cmd +
                              ' && ' + login_faas_cmd +
                              ' && ' + deploy_openfaas_cmd,
                              shell=True,
                              stdout=subprocess.PIPE,
                              cwd=function_dir,
                              encoding="utf-8").stdout
        # 6. remove function_name from except_list
        if function_name in except_list:
            except_list.remove(function_name)
        succeed = True
    except Exception as e:
        info = str(e)
        succeed = False
    finally:
        return JsonResponse({'succeed': succeed, 'info': info})


def invoke_function(request):
    # return result
    result = ''
    # parse parameters
    function_name = request.POST['function_name']
    function_input = request.POST['function_input']
    try:
        # invoke function
        openfaas_invoke_cmd = 'faas invoke -g 192.168.205.128:31112 %s' % function_name
        proc = subprocess.Popen(openfaas_invoke_cmd,
                                shell=True,
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE)
        # set input
        proc.stdin.write(function_input.encode('utf-8'))
        # get output
        result = bytes.decode(proc.communicate()[0])
    except:
        result = 'occur error'
    finally:
        return JsonResponse({'result': result})


def delete_function(request):
    # return result
    result = 'ok'
    # parse parameters
    function_name = request.POST['function_name']

    try:
        # 1. login openfaas
        login_faas_cmd = 'faas-cli login --username admin --password admin -g 192.168.205.128:31112'
        # 2. remove function
        openfaas_remove_function_cmd = 'faas remove -g 192.168.205.128:31112 %s' % function_name
        # 3. execute cmd
        var = subprocess.run(login_faas_cmd +
                             ' && ' + openfaas_remove_function_cmd,
                             shell=True,
                             stdout=subprocess.PIPE,
                             encoding="utf-8").stdout
        # 4. function_name append to except_list
        except_list.append(function_name)
        # # 4. wait
        # while True:
        #     function_infos = get_openfaas_functions_info()
        #     function_name_list = [function_info['name'] for function_info in function_infos]
        #     if function_name not in function_name_list:
        #         break
        #     time.sleep(0.5)

    except:
        result = 'error'
    finally:
        return JsonResponse({'result': result})


def get_code_editor_src(request):
    src = 'http://' + str(request.environ['HTTP_HOST']).split(':')[0] + ":" + "30443"
    return JsonResponse({'code_editor_src': src})


def free_code_editor(request):
    # parse parameter
    username = request.POST['username']
    function_name = request.POST['function_name']

    # global parameter
    global code_editors_lifetime

    # set code_editors_lifetime
    if username not in code_editors_lifetime.keys():
        code_editors_lifetime[username] = {}
    if function_name not in code_editors_lifetime[username].keys():
        code_editors_lifetime[username] = {function_name: lifetime}

    # update lifetime
    code_editors_lifetime[username][function_name] = lifetime

    return JsonResponse({'result': 'ok'})


from login.views import user_login


def thread_free_code_editor(username):
    # global parameter
    global code_editors_lifetime
    global lifetime

    while True:
        try:
            if user_login[username]:
                print('thread_free_code_editor ' + username)
                for function_name in code_editors_lifetime[username].keys():
                    code_editors_lifetime[username][function_name] = code_editors_lifetime[username][function_name] - 1
                    if code_editors_lifetime[username][function_name] < 0:
                        # delete deployment
                        kube.delete_deployment('namespace-' + username, function_name)
                        print(
                            'delete deployment ,namespace: ' + 'namespace-' + username + ' function_name: ' + function_name)
            else:
                for function_name in code_editors_lifetime[username].keys():
                    kube.delete_deployment('namespace-' + username, function_name)
                break
        except:
            pass
        finally:
            time.sleep(600)


def pod_log_page(request):
    namespace = request.GET['namespace']
    function_name = request.GET['function_name']
    pods_log = kube.get_deployment_pod_log(namespace, 'faas_function=%s' % function_name)
    return render(request, 'function-log.html',
                  {'function_name': function_name, 'pods_log': pods_log, 'pods_size': len(pods_log)})


def read_function_yml(request):
    # parse parameter
    username = request.POST['username']
    function_name = request.POST['function_name']
    # join file path
    BASE_DIR = Path(__file__).resolve().parent.parent
    file_path = str(BASE_DIR) + '/' + 'workspace' + '/' + username + '/' + function_name + '/' + function_name + '.yml'
    # read from file
    with open(file_path, 'r') as doc:
        data = yaml.load(doc, Loader=yaml.Loader)
    return JsonResponse(data)


def write_function_yml(request):
    # parse parameter
    username = request.POST['username']
    function_name = request.POST['function_name']
    data = json.loads(request.POST['data'])
    # process
    data['version'] = 1.0
    # join file path
    BASE_DIR = Path(__file__).resolve().parent.parent
    file_path = str(BASE_DIR) + '/' + 'workspace' + '/' + username + '/' + function_name + '/' + function_name + '.yml'

    with open(file_path, 'w', encoding='utf-8') as f:
        yaml.dump(data, f, Dumper=yaml.RoundTripDumper)
    return JsonResponse({'succeed': True})


def delete_service(request):
    username = request.GET['username']
    try:
        kube.delete_dynamic_service('namespace-'+username, 'service-'+username)
        return JsonResponse({'succeed': True})
    except:
        return JsonResponse({'succeed': False})


def list_cluster_node(request):
    return JsonResponse({'r':'ok'})